<template>
  <!-- 树组件的递归 -->
  <div class="navLeft">
    <div class="line w-100" style="padding-left:20px;" v-for="(item,i) in props.data" :key="'elTree'+i">
      <div class="d-flex ai-center">
        <i v-if="!item.load" :class="[ inCludes(lookup(item,props.treeProps.label)) ? 'el-icon-folder-remove':'el-icon-folder-add','icon']" @click="showCan(item)"></i>
        <i v-else class="el-icon-loading icon"></i>
        <el-checkbox v-if="props.checkBox" v-model="item.checkbox" @change="checkAll(item)"></el-checkbox>
        <i :class="item.icon"></i>
        <div :title="lookup(item,props.treeProps.label)" :class="['text', activeId == lookup(item,props.treeProps.value) ? 'active':'']" 
        @click="clickName(lookup(item,props.treeProps.value))">{{lookup(item,props.treeProps.label)}}</div>
      </div>
      <nav-tree
        class="animate__animated animate__fadeInDown fadeInDown"
        v-if="lookup(item,props.treeProps.children) && lookup(item,props.treeProps.children).length && inCludes(lookup(item,props.treeProps.value))"
        :data="lookup(item,props.treeProps.children)"
        :parentId="lookup(item,props.treeProps.value)"
        :Lever="Lever + i"
        :selectIds="data.selectIds"
        :activeId="activeId"
        :loadMore="props.loadMore"
        :treeProps="props.treeProps"
        :checkBox="props.checkBox"
        @clickNode="clickNode"
        @checkBox="checkBoxEmit"
      ></nav-tree>
    </div>
  </div>
</template>

<script lang="ts" setup>
export interface TreeList{
  name: string;
  id: string;
  icon?: string;
  children?: TreeList[],
  load?: boolean,
  checkbox: boolean,
  [propName: string]: any
}

interface data{
  selectIds: string[],
}

interface treeProps{
  label: string,
  value: string,
  children: string
  arr: string
}

import { defineProps, reactive, defineEmit, watch, ref, useContext } from 'vue';
import type { PropType } from "vue";
import { lookup, currying, copy } from '@/utils/ToolsClass/auth';

const emit = defineEmit(['clickNode','loadMore','checkBox'])

// 导出子组件需要暴露出去的方法
const { expose } = useContext()

// 传递进来的数据
const props = defineProps({
  data:{
    require:true,
    type: Array as PropType<TreeList[]>,
  },
  // 记录层级（很重要）
  Lever:{
    require:true,
    default: 0,
    type: Number
  },
  // 记住被勾选的值
  selectIds:{
    default:[],
    type: Array as PropType<string[]>
  },
  // 是否开启懒加载
  loadMore:{
    type: Function
  },
  // 高亮的id
  activeId:{
    default: '',
    type: String
  },
  // 设置请求获取数据的格式
  treeProps:{
    default:{ label:'name',value:'id', children:'children' , arr:'items' },
    type: Object as PropType<treeProps>
  },
  // 当前展开子类的父级id
  parentId:{
    type: String
  },
  // 是否开启复选框功能
  checkBox:{
    default: false,
    type: Boolean
  }
})
// 将选中的值逐级传递下去
let data:data = reactive({
  selectIds: props.selectIds,
})
// 判断是否已经被勾选
const showCan = (val:TreeList)=>{
  let index = data.selectIds.findIndex((v:string)=> v == val.id);
  index> -1 ? data.selectIds.splice(index,1) : data.selectIds.push(val.id);
  if(lookup(val,props.treeProps.children) && lookup(val,props.treeProps.children).length){
    return false
  }else{
    // 如果当前值下方有值，就不要在发送钩子回调出去
    if(!lookup(val,props.treeProps.children)){
      if(props.loadMore){
        val.load = true
        val[props.treeProps.children] = []
        props.loadMore().then((res:any)=>{
          let arr = lookup(res.data,props.treeProps.arr)
          if(arr && arr instanceof Array){
            val[props.treeProps.children] = arr;
            val.load = false;
            // 开启勾选时，懒加载的数据要和父节点选择的节点一致
            findChildren(val[props.treeProps.children],val?.checkbox || false)
          }
        })
      }
    }
    emit('loadMore',val.id)
  }
}

// 当前选中的id
let activeId = ref('')

// 传递出去你当前选择的值
const clickName = (id:string)=>{
  activeId.value = id
}

// 判断是否展开
const inCludes = (id:string): Boolean =>{
  return data.selectIds.includes(id)
}

// 监听值的变化
watch(()=> props.activeId,(n:string)=>{
  activeId.value = n
},{
  immediate:true
})

// 监听选中id的变化在逐级向上传递，不然会出现最后一层的id不能传递给最顶部
watch(activeId,(n:string)=>{
  emit('clickNode',n)
})

// 将递归组件中选择高亮的id逐级上传
const clickNode = (id:string)=>{
  activeId.value = id
}

// 勾选事件（当前子集如果全部被勾选，向上传递）
const checkAll = (item:TreeList) => {
  let checkType = props.data && props.data.some((v:TreeList)=> v.checkbox == false || v.checkbox == undefined);
  checkType ? emit('checkBox', item, props.parentId , false , lookup(item,props.treeProps.value)) : emit('checkBox', item, props.parentId , true, lookup(item,props.treeProps.value))
  // 对于一级菜单要单独处理，因为不会emit(checkBox)方法实现更新，只能手动触发
  if(props.Lever == 0){
    checkType ? checkBoxEmit(item, lookup(item,props.treeProps.value) , false , lookup(item,props.treeProps.value)) : checkBoxEmit(item, lookup(item,props.treeProps.value) , true , lookup(item,props.treeProps.value))
  }
}

// 接受子类传递的值高亮父级勾选框
const checkBoxEmit = (item:TreeList,parentId: string, type: boolean , nowId:string) =>{
  // 当所有子类全被勾选时，高亮父级,反之则取消高亮
  let index = props.data?.findIndex((v:TreeList)=> lookup(v,props.treeProps.value) == parentId);
  // 对于一级菜单要单独处理，因为不会emit(checkBox)方法实现更新，只能手动触发
  if(index != undefined &&  index > -1 && props.data && props.Lever > 0){
    if(type){
      props.data[index].checkbox = true;
    }else{
      props.data[index].checkbox = false;
    }
  }
  let model = findParent(nowId,props.data || [])
  if(model){
    let changeArr = lookup(model,props.treeProps.children)
    changeArr && findChildren(changeArr,model?.checkbox || false)
  }
}

// 递归高亮和取消其子集勾选复选框
const findChildren = (data:TreeList[],type:boolean)=>{
  data.forEach((v:TreeList)=>{
    v.checkbox = type;
    if(lookup(v,props.treeProps.children) && lookup(v,props.treeProps.children).length){
      findChildren(lookup(v,props.treeProps.children),type)
    }
  })
}

// 递归查询上级目录
const findParent = (val:string,data:TreeList[]) :TreeList | undefined =>{
  for(let i = 0;i< data.length;i++){
    if(lookup(data[i],props.treeProps.value) == val){
      return data[i]
    }else{
      if(lookup(data[i],props.treeProps.children)){
        return findParent(val,lookup(data[i],props.treeProps.children))
      }
    }
  }
}

// 获取被勾选的id
const getCheckAllId = () =>{
  let selectIdsAll = currying(copy(props.data))
  let arr:string[] =  [];
  selectIdsAll.forEach((v:TreeList)=>{
    if(v.checkbox){
      arr.push(lookup(v,props.treeProps.value))
    }
  })
  return {
    data: props.data, // 返回总的数据
    selectIdsAll: arr, // 返回被勾选的数据
  }
}

expose({
  getCheckAllId
})

</script>

<style lang="scss" scoped>
.navLeft{
  width: 100%;
  box-sizing: border-box;
  .line{
    position: relative;
    width: 100%;
    line-height: 25px;
    font-size: 13px;
    cursor: pointer;
    .icon{
      border-radius: 3px;
      font-size: 15px;
      padding: 2px;
      
    }
    .text{
      margin-left: 3px;
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
      width: 100%;
    }
    .active{
      color: $themeColor
    }
  }
}
</style>